/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Modelo;

/**
 *
 * @author Antonio Ariza
 */
import Vista.Inicio;
import Utils.CoordUTM;
import Utils.Util;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import java.util.Vector;
//import javax.microedition.io.Connector;
//import javax.microedition.io.file.FileConnection;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.FileOutputStream;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

public class GoogleMaps {

    //Clave de Google Maps
    String apiKey = null;
    //these 2 properties will be used with map scrolling methods. You can remove them if not needed
    int offset = 268435456;
    double radius = offset / Math.PI;
    public static int contador;
    int width;
    int height;
    int numMapas;
    String mapType;

    public GoogleMaps(String apiKey) {
        this.apiKey = apiKey;
        contador = 0;
        numMapas = 0;
    }

    public double[] geocodeAddress(String address) throws Exception {
        byte[] res = loadHttpFile(getGeocodeUrl(address));
        String resString = new String(res, 0, res.length);
        String[] data = split(resString, ',');
        if (data[0].compareTo("200") != 0) {
            int errorCode = Integer.parseInt(data[0]);
            throw new Exception("Google Maps Exception: " + getGeocodeError(errorCode));
        } else {
            return new double[]{
                        Double.parseDouble(data[2]),
                        Double.parseDouble(data[3])
                    };
        }
    }

    /**
     * Método que obtiene un mapa de Google Maps dados unos parámetros. Requiere
     * conexión a internet
     * @param width Anchura del mapa a descargar
     * @param height Altura del mapa a descargar
     * @param lat Latitud
     * @param lng Longitud
     * @param zoom Zoom
     * @param format Formato del mapa
     */
    public BufferedImage retrieveStaticImage(int width, int height, double lat, double lng, int zoom, String format, String mapType) throws Exception {
        return ImageIO.read(new URL(getMapUrl(width, height, lng, lat, zoom, format, mapType)));
    }

    String getGeocodeError(int errorCode) {
        switch (errorCode) {
            case 400:
                return "Bad request";
            case 500:
                return "Server error";
            case 601:
                return "Missing query";
            case 602:
                return "Unknown address";
            case 603:
                return "Unavailable address";
            case 604:
                return "Unknown directions";
            case 610:
                return "Bad API key";
            case 620:
                return "Too many queries";
            default:
                return "Generic error";
        }
    }

    String getGeocodeUrl(String address) {
        return "http://maps.google.com/maps/geo?q=" + urlEncode(address) + "&output=csv&key=" + apiKey;
    }

    /**
     * Método que construye la URL desde la que se descargará el mapa
     * @param width Anchura del mapa a descargar
     * @param height Altura del mapa a descargar
     * @param lat Latitud
     * @param lng Longitud
     * @param zoom Zoom
     * @param format Formato del mapa
     */
    String getMapUrl(int width, int height, double lng, double lat, int zoom, String format, String mapType) {
        return "http://maps.google.com/staticmap?center=" +

                lat + "," + lng + "&format=" + format + "&zoom=" + zoom + "&size=" +
                width + "x" + height + "&h=15" + "&maptype=" + mapType + "&key=" + apiKey;
    }


    String urlEncode(String str) {
        StringBuffer buf = new StringBuffer();
        char c;
        for (int i = 0; i < str.length(); i++) {
            c = str.charAt(i);
            if ((c >= '0' && c <= '9') ||
                    (c >= 'A' && c <= 'Z') ||
                    (c >= 'a' && c <= 'z')) {
                buf.append(c);
            } else {
                buf.append("%").append(Integer.toHexString((int) str.charAt(i)));
            }
        }
        return buf.toString();
    }

    byte[] loadHttpFile(String u) throws Exception {
        //Contador de numero de intentos realizados de lectura.
        URL url = new URL(u);
        int i = 0;
        boolean read = false;
        HttpURLConnection hc = null;
        InputStream is = null;
        byte[] byteBuffer = null;
        while ((!read) && (i < 4)) {
            read = true;
            i++;
            try {
                hc = (HttpURLConnection) url.openConnection();
                hc.setRequestMethod("GET");
                int ch;
                is = hc.getInputStream();
                int len = (int) hc.getContentLength();
                if (len > 0) {
                    byteBuffer = new byte[len];
                    is.read(byteBuffer);
                } else {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    while ((ch = is.read()) != -1) {
                        bos.write(ch);
                    }
                    byteBuffer = bos.toByteArray();
                    bos.close();
                }
            } catch (Exception e) {
                read = false;
                e.printStackTrace();
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                    if (hc != null) {
                        hc.disconnect();
                    }
                } catch (Exception e2) {
                    read = false;
                    e2.printStackTrace();
                }
            }
        }
        return byteBuffer;
    }

    /**
     * Método que divide una cadena de caracteres en varias cadenas. La división
     * se hace con respecto a un caracter que se toma como separador
     * @param s String a dividir
     * @param chr Caracter separador
     */
    public static String[] split(String s, int chr) {
        Vector res = new Vector();
        int curr = 0;
        int prev = 0;
        while ((curr = s.indexOf(chr, prev)) >= 0) {
            res.addElement(s.substring(prev, curr));
            prev = curr + 1;
        }
        res.addElement(s.substring(prev));
        String[] splitted = new String[res.size()];
        res.copyInto(splitted);
        return splitted;
    }

    /** Metodo que guarda una imagen en disco
     * @param imagen Imagen a almacenar en disco
     * @param rutaFichero Ruta de la imagen donde vamos a salvar la imagen
     * @param formato Formato de la imagen al almacenarla en disco
     * @return Booleano indicando si se consiguio salvar con exito la imagen
     */
    public boolean salvarImagen(final Image imagen, final String rutaFichero, final String formato) {
        // Comprobacion de parametros
        if (imagen != null && rutaFichero != null && formato != null) {
            try {
                ImageIO.write((RenderedImage) imagen, formato, new File(rutaFichero));
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        } else {
            // Fallo en los parametros
            return false;
        }
    }

    /**
     * Método que guarda una matriz de n X n mapas descargados de Google Maps,
     * generando un .map
     * @param n Tamaño de la matriz
     * @param matrizGmaps Matriz de mapas de Google Maps
     * @param anchoPantalla Anchura de la pantalla del dispositivo
     * @param altoPantalla Altura de la pantalla del dispositivo
     */
    public void guardar(int n, ElementoGmaps[][] matrizGmaps, int anchoPantalla, int altoPantalla) {
        numMapas = 0;
        String rutaMapa = "", nombreMapa = "";
        JFileChooser dialogGuardar = new JFileChooser();
        dialogGuardar.setDialogTitle("Guardar mapa");
        int seleccion = dialogGuardar.showSaveDialog(null);
        if (seleccion == dialogGuardar.APPROVE_OPTION) {
            rutaMapa = dialogGuardar.getSelectedFile().getPath();
            nombreMapa = dialogGuardar.getSelectedFile().getName();
        //}

        try {
            File f = new File(rutaMapa + numMapas + "" + 0 + "_" + 0 + ".png");
            while (f.exists()) {
                numMapas++;
                f = new File(rutaMapa + numMapas + "" + 0 + "_" + 0 + ".png");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        //Guardamos las imágenes
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                ElementoGmaps temp = matrizGmaps[i][j];
                Image image = temp.getImagen();
                try {
                    salvarImagen(image, rutaMapa + numMapas + "" + i + "_" + j + ".png", "png");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        //Generamos el .map
        nombreMapa = nombreMapa + numMapas;
        double escala = Inicio.getControlador().getGeomantes().getEscala();
        int zoom = Inicio.getControlador().getGeomantes().getNivelZoom();

        CoordUTM coordenadas = Utils.Util.LLtoUTM(23, matrizGmaps[0][0].getLatitud(), matrizGmaps[0][0].getLongitud());

        double latitudArribaIzq = coordenadas.getUTMNorthing() + (matrizGmaps[0][0].getImagen().getHeight(null) / 2) * escala;
        double longitudArribaIzq = coordenadas.getUTMEasting() - (matrizGmaps[0][0].getImagen().getWidth(null) / 2) * escala;
        coordenadas = Utils.Util.LLtoUTM(23, matrizGmaps[0][n - 1].getLatitud(), matrizGmaps[0][n - 1].getLongitud());
        double latitudArribaDer = coordenadas.getUTMNorthing() + (matrizGmaps[0][n - 1].getImagen().getHeight(null) / 2) * escala;
        double longitudArribaDer = coordenadas.getUTMEasting() + (matrizGmaps[0][n - 1].getImagen().getWidth(null) / 2) * escala;
        coordenadas = Utils.Util.LLtoUTM(23, matrizGmaps[n - 1][0].getLatitud(), matrizGmaps[n - 1][0].getLongitud());
        double latitudAbajoIzq = coordenadas.getUTMNorthing() - (matrizGmaps[n - 1][0].getImagen().getHeight(null) / 2) * escala;
        double longitudAbajoIzq = coordenadas.getUTMEasting() - (matrizGmaps[n - 1][0].getImagen().getWidth(null) / 2) * escala;
        coordenadas = Utils.Util.LLtoUTM(23, matrizGmaps[n - 1][n - 1].getLatitud(), matrizGmaps[n - 1][n - 1].getLongitud());
        double latitudAbajoDer = coordenadas.getUTMNorthing() - (matrizGmaps[n - 1][n - 1].getImagen().getHeight(null) / 2) * escala;
        double longitudAbajoDer = coordenadas.getUTMEasting() + (matrizGmaps[n - 1][n - 1].getImagen().getWidth(null) / 2) * escala;

        double[] latlon = Util.adjust(matrizGmaps[0][0].getLatitud(), matrizGmaps[0][0].getLongitud(),
                -(anchoPantalla / 2), -(altoPantalla / 2), zoom);

        double latitudArribaIzqGrados = latlon[1];
        double longitudArribaIzqGrados = latlon[0];

        latlon = Util.adjust(matrizGmaps[0][n - 1].getLatitud(), matrizGmaps[0][n - 1].getLongitud(),
                (anchoPantalla / 2), -(altoPantalla / 2), zoom);

        double latitudArribaDerGrados = latlon[1];
        double longitudArribaDerGrados = latlon[0];

        latlon = Util.adjust(matrizGmaps[n - 1][0].getLatitud(), matrizGmaps[n - 1][0].getLongitud(),
                -(anchoPantalla / 2), (altoPantalla / 2), zoom);

        double latitudAbajoIzqGrados = latlon[1];
        double longitudAbajoIzqGrados = latlon[0];

        latlon = Util.adjust(matrizGmaps[n - 1][n - 1].getLatitud(), matrizGmaps[n - 1][n - 1].getLongitud(),
                (anchoPantalla / 2), (altoPantalla / 2), zoom);

        double latitudAbajoDerGrados = latlon[1];
        double longitudAbajoDerGrados = latlon[0];

        try {
            File map = new File(rutaMapa + numMapas + ".map");
            map.createNewFile();
            FileOutputStream buffer = new FileOutputStream(map);

            String temp = "OziExplorer Map Data File Version 2.2\n";
            temp += nombreMapa + ".png\n";
            temp += nombreMapa + ".png\n";
            temp += "1 ,Map Code, \n";
            temp += "WGS 84,, 0.0000, 0.0000,WGS 84\n";
            temp += "Reserved 1\n";
            temp += "Reserved 2\n";
            temp += "Magnetic Variation,,,E\n";
            temp += "Map Projection,(UTM) Universal Transverse Mercator,PolyCal,No,AutoCalOnly,No,BSBUseWPX,No\n";
            temp += "Point01,xy,0,0,in, deg, , ,N, , ,W, grid, 30, " + longitudArribaIzq + "," + latitudArribaIzq + ",N\n";
            temp += "Point02,xy," + matrizGmaps[0][0].getImagen().getWidth(null) * n + ",0,in, deg, , ,N, , ,W, grid, 30, " + longitudArribaDer + ", " + latitudArribaDer + ",N\n";
            temp += "Point03,xy," + matrizGmaps[0][0].getImagen().getWidth(null) * n + "," + matrizGmaps[0][0].getImagen().getHeight(null) * n + ",in, deg, , ,N, , ,W, grid, 30, " + longitudAbajoDer + ", " + latitudAbajoDer + ",N\n";
            temp += "Point04,xy,0," + matrizGmaps[0][0].getImagen().getHeight(null) * n + ",in, deg, , ,N, , ,W, grid, 30, " + longitudAbajoIzq + ", " + latitudAbajoIzq + ",N\n";
            temp += "Projection Setup,,,,,,,,,,\n";
            temp += "Map Feature = MF ; Map Comment = MC     These follow if they exist\n";
            temp += "Track File = TF      These follow if they exist\n";
            temp += "Moving Map Parameters = MM?    These follow if they exist\n";
            temp += "MM0,Yes\n";
            temp += "MMPNUM,4\n";
            temp += "MMPXY,1,0,0\n";
            temp += "MMPXY,2," + matrizGmaps[0][0].getImagen().getWidth(null) * n + ",0\n";
            temp += "MMPXY,3," + matrizGmaps[0][0].getImagen().getWidth(null) * n + "," +
                    matrizGmaps[0][0].getImagen().getHeight(null) * n + "\n";
            temp += "MMPXY,4,0," + matrizGmaps[0][0].getImagen().getHeight(null) * n + "\n";
            temp += "MMPLL,1," + longitudArribaIzqGrados + "," + latitudArribaIzqGrados + "\n";
            temp += "MMPLL,2," + longitudArribaDerGrados + "," + latitudArribaDerGrados + "\n";
            temp += "MMPLL,3," + longitudAbajoDerGrados + "," + latitudAbajoDerGrados + "\n";
            temp += "MMPLL,4," + longitudAbajoIzqGrados + "," + latitudAbajoIzqGrados + "\n";
            temp += "MM1B," + escala + "\n";
            temp += "MOP,Map Open Position,0,0\n";
            temp += "IWH,Map Image Width/Height," + matrizGmaps[0][0].getImagen().getWidth(null) * n + "," +
                    matrizGmaps[0][0].getImagen().getHeight(null) * n;


            buffer.write(temp.getBytes());
            buffer.flush();
            buffer.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  }
}


